#include "widget.h"
#include "./ui_widget.h"
#include <QtSerialPort/QSerialPortInfo>
#include <QMessageBox>
#include <QModbusRtuSerialClient>
#include <QMetaEnum>
#include <QAbstractItemView>
#include <string.h>
#include "crc.h"


Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->setWindowTitle("盐机Modbus测试");
    modbusDevice = new QModbusRtuSerialClient(this);
    // 端口填充
    QStringList baudRate;
    for(auto const &i : m_baudRateList)
    {
        baudRate.append(i.first);
    }
    ui->cb_serialBaudrate->addItems(baudRate);
    ui->cb_serialBaudrate->setCurrentIndex(1);

    QStringList parity;
    for(auto const &i : m_parityList)
    {
        parity.append(i.first);
    }
    ui->cb_serialParity->addItems(parity);
    ui->sp_modebusTimeout->setSuffix(" ms");
    ui->sp_modebusTimeout->setValue(1000);
    ui->sp_modbusRetry->setSuffix(" 次");
    ui->sp_modbusRetry->setValue(0);
    Fill_SerialPort();
    connect(&m_serialPortTimer,&QTimer::timeout,this,&Widget::Fill_SerialPort);
    connect(&m_autoReadTimer,&QTimer::timeout,this,&Widget::on_auto_read_timeout);
    // 设备填充
    ui->sp_modbusAddress->setValue(8);
    QStringList chlorinatorCfg = {
        "F1",
        "F2",
        "F3",
        "F4"
    };
    ui->cb_chlorinatorCfg->addItems(chlorinatorCfg);
    QStringList electrolytic = {
        "10g",
        "16g",
        "22g",
        "28g"
    };
    ui->cb_electrolytic->addItems(electrolytic);
    ui->cb_electrolytic->setCurrentIndex(1);
    ui->le_icid->setEnabled(false);
    m_serialPortTimer.start(1000);


    // 操作填充
    modbus_item_init();
    ui->cb_modbusDataType->setCurrentIndex(input_registers);

}

Widget::~Widget()
{
    delete ui;

}

void Widget::Fill_SerialPort(void)
{
    static QStringList lastPortInfo;
    QStringList portInfo;
    if(modbusDevice->state() != QModbusDevice::ConnectedState)
    {

        for(auto const &info : QSerialPortInfo::availablePorts())
        {
            portInfo.append(info.portName());
        }
        if(lastPortInfo != portInfo)
        {
            lastPortInfo = portInfo;
            ui->cb_serialPort->clear();
            ui->cb_serialPort->addItems(portInfo);
        }
    }
}

void Widget::modbus_item_init(void)
{
    QMetaEnum metaEnum = QMetaEnum::fromType<modbus_dataTypes>();
    int nEnumNum = metaEnum.keyCount();
    for(int i=0;i<nEnumNum;i++)
    {
        const char* s = metaEnum.valueToKey(i);
        QString cbItem(s);
        ui->cb_modbusDataType->addItem(cbItem);
        m_dataItems[i].type = (modbus_dataTypes)i;
        modbus_item_type_init((modbus_dataTypes)i);
    }
}

void Widget::modbus_item_coils_init(void)
{

}
void Widget::modbus_item_discrete_inputs_init(void)
{
    QMetaEnum metaEnum;
    metaEnum = QMetaEnum::fromType<discrete_inputs_item>();
    ui->cb_modbusDataName->clear();
    int nEnumNum = metaEnum.keyCount();
    m_dataItems[discrete_inputs].number = nEnumNum;
    for(int i=0;i<nEnumNum;i++)
    {
        int enumValue = i;
        if(i > ERR_GAS_SW)
        {
            enumValue += ALARM_ACID_TANK;
            enumValue -= (ERR_GAS_SW+1);
        }

        const char* s = metaEnum.valueToKey(enumValue);
        QString cbItem(s);
        ui->cb_modbusDataName->addItem(cbItem);
        m_dataItems[discrete_inputs].items.append(cbItem);

        // 添加显示控件
        discrete_input_display_t did;
        did.lb = new QLabel(ui->gb_discrete_inputs);
        did.lb->setText(cbItem);
        did.lb->setGeometry(QRect(10,i*30+20,140,25));
        did.sp = new QSpinBox(ui->gb_discrete_inputs);
        did.sp->setGeometry(QRect(150,i*30+20,80,25));
        did.sp->setMaximum(10000);
        did.sp->setEnabled(false);
        m_discreteInputDisplay.append(did);
    }
}
void Widget::modbus_item_input_registers_init(void)
{
    QMetaEnum metaEnum;
    metaEnum = QMetaEnum::fromType<input_registers_item>();
    ui->cb_modbusDataName->clear();
    int nEnumNum = metaEnum.keyCount();
    m_dataItems[input_registers].number = nEnumNum;
    for(int i=0;i<nEnumNum - inputFaNum;i++)
    {
        const char* s = metaEnum.valueToKey(i);
        QString cbItem(s);
        ui->cb_modbusDataName->addItem(cbItem);
        m_dataItems[input_registers].items.append(cbItem);

        // 添加显示控件
        input_register_display_t ird;
        ird.lb = new QLabel(ui->gb_input_registers);
        ird.lb->setText(cbItem);
        ird.lb->setGeometry(QRect(10,i*30+20,140,25));
        ird.sp = new QSpinBox(ui->gb_input_registers);
        ird.sp->setGeometry(QRect(150,i*30+20,80,25));
        ird.sp->setMaximum(32767);
        ird.sp->setMinimum(-32738);
        ird.sp->setEnabled(false);
        m_inputRegisterDisplay.append(ird);
    }

}
void Widget::modbus_item_holding_registers_init(void)
{
    QMetaEnum metaEnum;
    metaEnum = QMetaEnum::fromType<holding_registers_item>();
    ui->cb_modbusDataName->clear();
    int nEnumNum = metaEnum.keyCount();
    m_dataItems[holding_registers].number = nEnumNum;
    for(int i=0;i<nEnumNum - holdingFaNum;i++)
    {
        const char* s = metaEnum.valueToKey(i);
        QString cbItem(s);
        ui->cb_modbusDataName->addItem(cbItem);
        m_dataItems[holding_registers].items.append(cbItem);

        // 添加显示控件

        holding_register_display_t hrd;
        hrd.lb = new QLabel(ui->gb_holding_registers);
        hrd.lb->setText(cbItem);
        hrd.lb->setGeometry(QRect(10,i*30+20,140,25));
        if((i >= OPEN_TIMER1 && i <= CLOSE_TIMER2) || i == LOCAL_TIME)
        {
            hrd.te = new QTimeEdit(ui->gb_holding_registers);
            hrd.te->setGeometry(QRect(150,i*30+20,80,25));
            hrd.te->setDisplayFormat("HH:mm");
            hrd.te->setTimeRange(QTime(0,0),QTime(23,59));
            hrd.te->setTime(QTime(0,0));

        }
        else
        {
            hrd.sp = new QSpinBox(ui->gb_holding_registers);
            hrd.sp->setGeometry(QRect(150,i*30+20,80,25));
            hrd.sp->setMaximum(32767);
            hrd.sp->setMinimum(-32768);
        }
        m_holdingRegisterDisplay.append(hrd);
    }
}

void Widget::modbus_item_type_init(Widget::modbus_dataTypes type)
{
    switch (type) {
    case coils:
        modbus_item_coils_init();
        break;

    case discrete_inputs:
        modbus_item_discrete_inputs_init();
        break;

    case input_registers:
        modbus_item_input_registers_init();
        break;

    case holding_registers:
        modbus_item_holding_registers_init();
        break;
    default:
        return;
    }
}

void Widget::modbus_operate_display(modbus_dataTypes type)
{
    ui->cb_modbusDataName->clear();
    ui->cb_modbusDataName->addItems(m_dataItems[type].items);
    ui->sp_modbusNum->setMinimum(1);
    ui->sp_modbusNum->setMaximum(m_dataItems[type].number);
    if(type == discrete_inputs || type == input_registers)
    {
        ui->pb_modbusWrite->setEnabled(false);
    }
    else
    {
        ui->pb_modbusWrite->setEnabled(true);
    }
}


void Widget::on_pb_openPort_clicked()
{
    if(ui->pb_openPort->text() == "连接")
    {
        modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,
                                             ui->cb_serialPort->currentText());
        modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,
                                             m_parityList.at(ui->cb_serialParity->currentIndex()).second);
        modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,
                                             m_baudRateList.at(ui->cb_serialBaudrate->currentIndex()).second);
        modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,
                                             QSerialPort::Data8);
        modbusDevice->setTimeout(ui->sp_modebusTimeout->value());
        modbusDevice->setNumberOfRetries(ui->sp_modbusRetry->value());
        if (modbusDevice->connectDevice())
        {
            ui->pb_openPort->setText("断开连接");
        }
        else
        {
            QString err = "连接失败:";
            err.append('\n');
            err.append(modbusDevice->errorString());
            QMessageBox::critical(this,"错误",err);
        }
    }
    else
    {
        modbusDevice->disconnectDevice();
        ui->pb_openPort->setText("连接");
    }
}

void Widget::Calc_PassWord(void)
{
    uint8_t crcBuf[8];
    m_crc32Icid = crc32((uint8_t *)m_icid, 12);
    qDebug()<<QString::number(m_crc32Icid,16);
    memcpy(crcBuf,(uint8_t*)&m_crc32Icid,4);
    memset(&crcBuf[4],0xFA,4);
    m_password = crc16(crcBuf,8);
    qDebug()<<QString::number(m_password,16);
    QString icid;
    QString temp;
    temp = QString("%1").arg(m_icid[5],4,16,QLatin1Char('0'));
    icid.append(temp.toUpper());
    temp = QString("%1").arg(m_icid[4],4,16,QLatin1Char('0'));
    icid.append(temp.toUpper());
    temp = QString("%1").arg(m_icid[3],4,16,QLatin1Char('0'));
    icid.append(temp.toUpper());
    temp = QString("%1").arg(m_icid[2],4,16,QLatin1Char('0'));
    icid.append(temp.toUpper());
    temp = QString("%1").arg(m_icid[1],4,16,QLatin1Char('0'));
    icid.append(temp.toUpper());
    temp = QString("%1").arg(m_icid[0],4,16,QLatin1Char('0'));
    icid.append(temp.toUpper());
    ui->le_icid->setText(icid);
}

void Widget::on_cb_modbusDataType_currentIndexChanged(int index)
{
    modbus_dataTypes type = (modbus_dataTypes)index;
    modbus_operate_display(type);
}


void Widget::on_pb_modbusRead_clicked()
{
    if (!modbusDevice)
        return;
    ui->lb_tips->clear();

    if(ui->cb_modbusDataType->currentText() == "discrete_inputs")
    {
        Read_DiscreteInputs();
    }
    else if(ui->cb_modbusDataType->currentText() == "coils")
    {
        Read_Coils();
    }
    else if(ui->cb_modbusDataType->currentText() == "input_registers")
    {
        Read_InputRegisters();
    }
    else if(ui->cb_modbusDataType->currentText() == "holding_registers")
    {
        Read_HoldingRegisters();
    }
}

void Widget::on_pb_modbusWrite_clicked()
{
    QTime t;
    uint16_t value;
    if (!modbusDevice)
        return;
    ui->lb_tips->clear();
    if(ui->cb_modbusDataType->currentText() == "holding_registers")
    {
        Write_HoldingRegisters();
    }
}

void Widget::on_cb_autoRead_checkStateChanged(const Qt::CheckState &arg1)
{
    if(arg1 == Qt::Unchecked)
    {
        m_autoReadTimer.stop();
    }
    else if(arg1 == Qt::Checked)
    {
        m_autoReadTimer.start(ui->sp_modebusTimeout->value());
    }
}

void Widget::on_auto_read_timeout(void)
{
    on_pb_modbusRead_clicked();
}

void Widget::on_pb_uidRead_clicked()
{
    if (!modbusDevice)
        return;
    int startAddress = IC_ID1;
    m_readNums = inputFaNum;    // 一共6个ID
    ui->le_icid->clear();
    //! [read_data_1]
    if (auto *reply = modbusDevice->sendReadRequest(
            QModbusDataUnit(QModbusDataUnit::InputRegisters,startAddress,m_readNums),
                                                    ui->sp_modbusAddress->value())) {
        if (!reply->isFinished())
            connect(reply, &QModbusReply::finished, this, &Widget::On_InputRegisters_Ready);
        else
            delete reply; // broadcast replies return immediately
    } else {
        QMessageBox::warning(this,"警告",modbusDevice->errorString());
    }
}


void Widget::on_pb_cfgWrite_clicked()
{
    uint16_t value;
    if (!modbusDevice)
        return;
    ui->lb_tips->clear();
    int startAddress = FA_PASSWORD;
    m_readNums = holdingFaNum;
    QModbusDataUnit writeUnit(QModbusDataUnit::HoldingRegisters, startAddress, m_readNums);

    writeUnit.setValue(0,m_password);
    writeUnit.setValue(1,ui->cb_chlorinatorCfg->currentIndex());
    switch(ui->cb_electrolytic->currentIndex())
    {
    case 0:
        value = 10;
        break;
    case 1:
        value = 20;
        break;
    case 2:
        value = 30;
        break;
    case 3:
        value = 40;
        break;
    default:
        value = 20;
        break;

    }
    writeUnit.setValue(2,value);

    if (auto *reply = modbusDevice->sendWriteRequest(writeUnit,
                                                     ui->sp_modbusAddress->value())) {
        if (!reply->isFinished()) {
            connect(reply, &QModbusReply::finished, this, &Widget::On_Write_Reply);
        } else {
            // broadcast replies return immediately
            reply->deleteLater();
        }
    } else {
        ui->lb_tips->setText(QString("Write error: %1").\
                             arg(modbusDevice->errorString()));
    }
}

void Widget::On_Write_Reply()
{
    auto reply = qobject_cast<QModbusReply *>(sender());
    if (!reply)
        return;

    const auto error = reply->error();
    if (error == QModbusDevice::ProtocolError) {
        ui->lb_tips->setText(QString("Write response error: %1 (Modbus exception: 0x%2)")
                                  .arg(reply->errorString()).arg(reply->rawResult().exceptionCode()));
    } else if (error != QModbusDevice::NoError) {
        ui->lb_tips->setText(QString("Write response error: %1 (code: 0x%2)")
                                  .arg(reply->errorString()).arg(error, -1, 16));

    }
    else
    {

    }
    reply->deleteLater();
}

void Widget::Read_DiscreteInputs(void)
{
    QMetaEnum metaEnum;
    if (!modbusDevice)
        return;
    ui->lb_tips->clear();
    metaEnum = QMetaEnum::fromType<discrete_inputs_item>();
    int startAddress = metaEnum.keyToValue(\
        ui->cb_modbusDataName->currentText().toLocal8Bit().constData());

    m_readNums = ui->sp_modbusNum->value();
    //! [read_data_1]
    if (auto *reply = modbusDevice->sendReadRequest(
            QModbusDataUnit(QModbusDataUnit::DiscreteInputs,startAddress,m_readNums),
            ui->sp_modbusAddress->value())) {
        if (!reply->isFinished())
            connect(reply, &QModbusReply::finished, this, &Widget::On_DiscreteInputs_Ready);
        else
            delete reply; // broadcast replies return immediately
    } else {
        QMessageBox::warning(this,"警告",modbusDevice->errorString());
        if(ui->cb_autoRead->isChecked())
        {
            ui->cb_autoRead->setCheckState(Qt::Unchecked);
        }
    }
}
void Widget::On_DiscreteInputs_Ready(void)
{
    uint16_t value;
    auto reply = qobject_cast<QModbusReply *>(sender());
    if (!reply)
        return;

    if (reply->error() == QModbusDevice::NoError) {
        const QModbusDataUnit unit = reply->result();
        int addr = 0;
        for (qsizetype i = 0, total = m_readNums; i < total; ++i) {
            addr = unit.startAddress() + i;
            const QString entry = tr("Address: %1, Value: %2").arg(unit.startAddress() + i)
                                      .arg(QString::number(unit.value(i),
                                                           unit.registerType() <= QModbusDataUnit::Coils ? 16 : 10));
            ui->lb_tips->setText(entry);
            if(addr <= ERR_GAS_SW)
            {
                m_discreteInputDisplay.at(addr).sp->setValue(unit.value(i));
            }
            else
            {
                m_discreteInputDisplay.at(addr - ALARM_ACID_TANK + ERR_GAS_SW + 1).sp->setValue(unit.value(i));
            }
        }
    } else if (reply->error() == QModbusDevice::ProtocolError) {
        if(ui->cb_autoRead->isChecked())
        {
            ui->cb_autoRead->setCheckState(Qt::Unchecked);
        }

        const QString entry = QString("Read response error: %1 (Modbus exception: 0x%2)").
                              arg(reply->errorString()).
                              arg(reply->rawResult().exceptionCode());
        ui->lb_tips->setText(entry);
    } else {
        if(ui->cb_autoRead->isChecked())
        {
            ui->cb_autoRead->setCheckState(Qt::Unchecked);
        }
        const QString entry = QString("Read response error: %1 (code: 0x%2)").
                              arg(reply->errorString()).
                              arg(reply->error());
        ui->lb_tips->setText(entry);
    }

    reply->deleteLater();
}
void Widget::Read_Coils(void)
{

}
void Widget::On_Coils_Ready(void)
{

}
void Widget::Read_InputRegisters(void)
{
    QMetaEnum metaEnum;
    if (!modbusDevice)
        return;
    ui->lb_tips->clear();
    metaEnum = QMetaEnum::fromType<input_registers_item>();
    int startAddress = metaEnum.keyToValue(\
                                           ui->cb_modbusDataName->currentText().toLocal8Bit().constData());

    m_readNums = ui->sp_modbusNum->value();
    //! [read_data_1]
    if (auto *reply = modbusDevice->sendReadRequest(
            QModbusDataUnit(QModbusDataUnit::InputRegisters,startAddress,m_readNums),
            ui->sp_modbusAddress->value())) {
        if (!reply->isFinished())
            connect(reply, &QModbusReply::finished, this, &Widget::On_InputRegisters_Ready);
        else
            delete reply; // broadcast replies return immediately
    } else {
        QMessageBox::warning(this,"警告",modbusDevice->errorString());
        if(ui->cb_autoRead->isChecked())
        {
            ui->cb_autoRead->setCheckState(Qt::Unchecked);
        }
    }
}
void Widget::On_InputRegisters_Ready(void)
{
    uint16_t value;
    auto reply = qobject_cast<QModbusReply *>(sender());
    if (!reply)
        return;

    if (reply->error() == QModbusDevice::NoError) {
        const QModbusDataUnit unit = reply->result();
        int addr = 0;
        for (qsizetype i = 0, total = m_readNums; i < total; ++i) {
            addr = unit.startAddress() + i;
            const QString entry = tr("Address: %1, Value: %2").arg(unit.startAddress() + i)
                                      .arg(QString::number(unit.value(i),
                                                           unit.registerType() <= QModbusDataUnit::Coils ? 16 : 10));
            ui->lb_tips->setText(entry);
            if(addr >= IC_ID1)
            {
                m_icid[addr - IC_ID1] = unit.value(i);
                qDebug()<<QString::number(m_icid[addr - IC_ID1],16);
                if(addr == IC_ID6)
                {
                    Calc_PassWord();
                }
            }
            else
            {
                m_inputRegisterDisplay.at(addr).sp->setValue(unit.value(i));
            }
        }
    } else if (reply->error() == QModbusDevice::ProtocolError) {
        if(ui->cb_autoRead->isChecked())
        {
            ui->cb_autoRead->setCheckState(Qt::Unchecked);
        }

        const QString entry = QString("Read response error: %1 (Modbus exception: 0x%2)").
                              arg(reply->errorString()).
                              arg(reply->rawResult().exceptionCode());
        ui->lb_tips->setText(entry);
    } else {
        if(ui->cb_autoRead->isChecked())
        {
            ui->cb_autoRead->setCheckState(Qt::Unchecked);
        }
        const QString entry = QString("Read response error: %1 (code: 0x%2)").
                              arg(reply->errorString()).
                              arg(reply->error());
        ui->lb_tips->setText(entry);
    }

    if(m_inputRegisterDisplay.at(U).sp->value() != 0)
    {
        uint16_t value = 1000.0*m_inputRegisterDisplay.at(I).sp->value()/m_inputRegisterDisplay.at(U).sp->value();
        ui->sp_iu1000->setValue(value);
    }

    reply->deleteLater();
}
void Widget::Read_HoldingRegisters(void)
{
    QMetaEnum metaEnum;
    if (!modbusDevice)
        return;
    ui->lb_tips->clear();
    metaEnum = QMetaEnum::fromType<holding_registers_item>();
    int startAddress = metaEnum.keyToValue(\
        ui->cb_modbusDataName->currentText().toLocal8Bit().constData());

    m_readNums = ui->sp_modbusNum->value();
    //! [read_data_1]
    if (auto *reply = modbusDevice->sendReadRequest(
            QModbusDataUnit(QModbusDataUnit::HoldingRegisters,startAddress,m_readNums),
            ui->sp_modbusAddress->value())) {
        if (!reply->isFinished())
            connect(reply, &QModbusReply::finished, this, &Widget::On_HoldingRegisters_Ready);
        else
            delete reply; // broadcast replies return immediately
    } else {
        QMessageBox::warning(this,"警告",modbusDevice->errorString());
        if(ui->cb_autoRead->isChecked())
        {
            ui->cb_autoRead->setCheckState(Qt::Unchecked);
        }
    }
}
void Widget::On_HoldingRegisters_Ready(void)
{
    uint16_t value;
    auto reply = qobject_cast<QModbusReply *>(sender());
    if (!reply)
        return;

    if (reply->error() == QModbusDevice::NoError) {
        const QModbusDataUnit unit = reply->result();
        int addr = 0;
        for (qsizetype i = 0, total = m_readNums; i < total; ++i) {
            addr = unit.startAddress() + i;
            const QString entry = tr("Address: %1, Value: %2").arg(unit.startAddress() + i)
                                      .arg(QString::number(unit.value(i),
                                                           unit.registerType() <= QModbusDataUnit::Coils ? 16 : 10));
            ui->lb_tips->setText(entry);
            value  = unit.value(i);
            if((addr >= OPEN_TIMER1 && addr <= CLOSE_TIMER2) ||
                addr == LOCAL_TIME)
            {
                QTime t = QTime(value>>8,value&0xFF);
                m_holdingRegisterDisplay.at(addr).te->setTime(t);
            }
            else
            {
                m_holdingRegisterDisplay.at(addr).sp->setValue(value);
            }
        }
    } else if (reply->error() == QModbusDevice::ProtocolError) {
        if(ui->cb_autoRead->isChecked())
        {
            ui->cb_autoRead->setCheckState(Qt::Unchecked);
        }

        const QString entry = QString("Read response error: %1 (Modbus exception: 0x%2)").
                              arg(reply->errorString()).
                              arg(reply->rawResult().exceptionCode());
        ui->lb_tips->setText(entry);
    } else {
        if(ui->cb_autoRead->isChecked())
        {
            ui->cb_autoRead->setCheckState(Qt::Unchecked);
        }
        const QString entry = QString("Read response error: %1 (code: 0x%2)").
                              arg(reply->errorString()).
                              arg(reply->error());
        ui->lb_tips->setText(entry);
    }

    reply->deleteLater();
}

void Widget::Write_HoldingRegisters(void)
{
    QTime t;
    uint16_t value;
    if (!modbusDevice)
        return;
    QMetaEnum metaEnum;
    metaEnum = QMetaEnum::fromType<holding_registers_item>();
    int startAddress = metaEnum.keyToValue(\
        ui->cb_modbusDataName->currentText().toLocal8Bit().constData());
    quint16 m_writeNums = ui->sp_modbusNum->value();
    ui->lb_tips->clear();
    QModbusDataUnit writeUnit(QModbusDataUnit::HoldingRegisters, startAddress,
                              m_writeNums);
    for (qsizetype i = 0, total = writeUnit.valueCount(); i < total; ++i)
    {
        const auto addr = i + writeUnit.startAddress();
        if((addr >= OPEN_TIMER1 && addr <= CLOSE_TIMER2) ||
            addr == LOCAL_TIME)
        {
            t = m_holdingRegisterDisplay.at(addr).te->time();
            value = (t.hour()<<8|t.minute());
            writeUnit.setValue(i, value);
        }
        else
        {
            value = m_holdingRegisterDisplay.at(addr).sp->value();
            writeUnit.setValue(i, value);
        }
    }

    if (auto *reply = modbusDevice->sendWriteRequest(writeUnit,
                                                     ui->sp_modbusAddress->value())) {
        if (!reply->isFinished()) {
            connect(reply, &QModbusReply::finished, this, &Widget::On_Write_Reply);
        } else {
            // broadcast replies return immediately
            reply->deleteLater();
        }
    } else {
        ui->lb_tips->setText(QString("Write error: %1").\
                             arg(modbusDevice->errorString()));
    }
}

